/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package org.flower.httprc.http;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.flower.httprc.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author ZhuYanXin
 */
public class HttpRClientX {
    private static final Logger LOG = LoggerFactory.getLogger(HttpRClientX.class);

    public HttpResp execute(HttpReq req) throws IOException {
        HttpResp httpResp = new HttpResp();
        try ( CloseableHttpClient httpClient = HttpClients.createDefault()) {
            String uri = encodeUriParameter(req.getUri());
            HttpRequestBase request = createRequest(uri, req.getMethod());
            configTimeout(request);
            configHeader(request, req.getHeader());
            AccessType accessType = AccessType.find(req.getContentType());
            switch (accessType) {
                case APPLICATION_FORM_URLENCODED:
                    applicationForm(request, accessType, req.getHeader());
                    break;
                case MULTIPART_FORM_DATA:
                    multipartFormData(request, accessType, req.getFilesKey(), req.getFormParam(), req.getLstFile());
                    break;
                case TEXT_PLAIN:
                case APPLICATION_JSON:
                default:
                    applicationJson(request, accessType, req.getJsonParam());
                    break;
            }

            try ( CloseableHttpResponse resp = httpClient.execute(request)) {
                Header[] reqHeaders = request.getAllHeaders();
                Map<String, String> mp = new HashMap<>();
                for (Header header : reqHeaders) {
                    mp.put(header.getName(), header.getValue());
                }
                httpResp.setProtocol(resp.getProtocolVersion().toString());
                int statusCode = resp.getStatusLine().getStatusCode();
                httpResp.setStatusCode(statusCode);

                Map<String, String> mapH = new HashMap<>();
                Header[] allHeaders = resp.getAllHeaders();
                for (Header header : allHeaders) {
                    mapH.put(header.getName(), header.getValue());
                }
                httpResp.setHeader(mapH);
                LOG.info("resp:{}", resp);
                String respBody = "";
                HttpEntity entity = resp.getEntity();
                if (entity != null) {
                    respBody = EntityUtils.toString(entity, "utf-8");
                }
                LOG.info("resp.body:{}", respBody);
                httpResp.setBody(respBody);
            }
        }
        return httpResp;
    }

    private String encodeUriParameter(String uri) {
        return uri;
    }

    private void applicationJson(HttpRequestBase request, AccessType contentType, String json) {
        if (contentType != AccessType.APPLICATION_JSON || !JsonUtil.validJson(json)) {
            return;
        }
        StringEntity entity = new StringEntity(json, Consts.UTF_8);
        if (request instanceof HttpPost) {
            HttpPost httpPost = (HttpPost) request;
            httpPost.setEntity(entity);
        }
    }

    private void applicationForm(HttpRequestBase request, AccessType contentType, Map<String, String> formParam) {
        if (contentType != AccessType.APPLICATION_FORM_URLENCODED) {
            return;
        }
        List<BasicNameValuePair> params = new ArrayList<>();
        for (Map.Entry<String, String> entry : formParam.entrySet()) {
            String key = entry.getKey();
            String val = entry.getValue();
            BasicNameValuePair param = new BasicNameValuePair(key, val);
            params.add(param);
        }
        UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(params, Consts.UTF_8);
        if (request instanceof HttpPost) {
            HttpPost httpPost = (HttpPost) request;
            httpPost.setEntity(formEntity);
        }
    }

    private void multipartFormData(HttpRequestBase request, AccessType contentType, String filesKey, Map<String, String> formParam, List<File> lstFile) {
        if (contentType != AccessType.MULTIPART_FORM_DATA) {
            return;
        }
        MultipartEntityBuilder meBuilder = MultipartEntityBuilder.create();
        for (File file : lstFile) {
            String encodeName = URLEncoder.encode(file.getName(), Consts.UTF_8);
            meBuilder.addBinaryBody(filesKey, file, ContentType.DEFAULT_BINARY, encodeName);
        }

        ContentType paramContentType = ContentType.create(AccessType.TEXT_PLAIN.value(), Consts.UTF_8);
        for (Map.Entry<String, String> entry : formParam.entrySet()) {
            String key = entry.getKey();
            String val = entry.getValue();
            meBuilder.addTextBody(key, val, paramContentType);
        }
        HttpEntity entity = meBuilder.build();
        if (request instanceof HttpPost) {
            HttpPost httpPost = (HttpPost) request;
            httpPost.setEntity(entity);
        }
    }

    private void configHeader(HttpRequestBase request, Map<String, String> header) {
        for (Map.Entry<String, String> entry : header.entrySet()) {
            String key = entry.getKey();
            String val = entry.getValue();
            request.setHeader(key, val);
        }
    }

    private void configTimeout(HttpRequestBase request) {
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(10000)
                .setConnectionRequestTimeout(10000)
                .setSocketTimeout(10000)
                .setRedirectsEnabled(true)
                .build();
        request.setConfig(config);
    }

    private HttpRequestBase createRequest(String uri, String reqMethod) {
        HttpMethod method = HttpMethod.valueOf(reqMethod);
        HttpRequestBase request;
        switch (method) {
            case OPTIONS:
                request = new HttpOptions(uri);
                break;
            case GET:
                request = new HttpGet(uri);
                break;
            case HEAD:
                request = new HttpHead(uri);
                break;
            case POST:
                request = new HttpPost(uri);
                break;
            case MULTIPART:
                request = new HttpPost(uri);
                break;
            case PUT:
                request = new HttpPut(uri);
                break;
            case DELETE:
                request = new HttpDelete(uri);
                break;
            case TRACE:
                request = new HttpTrace(uri);
                break;
            default:
                request = new HttpPost(uri);
                break;
        }
        return request;
    }
}
